package com.openjfx.database.app.controller;

import com.openjfx.database.app.AbstractController;
import com.openjfx.database.app.DatabaseFX;
import com.openjfx.database.app.config.DbPreference;
import com.openjfx.database.app.model.TableTransferModel;
import com.openjfx.database.app.utils.DialogUtils;
import com.openjfx.database.common.utils.StringUtils;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import net.sf.jsqlparser.JSQLParserException;

import java.util.stream.Collectors;

/**
 * Database table move / copy stage controller
 *
 * @author yangkui
 * @since 1.1
 */
public class TableTransferController extends AbstractController<TableTransferModel> {
    @FXML
    private Button back;
    @FXML
    private Button next;
    @FXML
    private Pagination pagination;

    private final HBox selectorBox = new HBox();
    private final TextArea textArea = new TextArea();
    private final BorderPane infoPage = new BorderPane();
    private final ProgressBar bar = new ProgressBar(0d);
    private final ToggleGroup toggleGroup = new ToggleGroup();
    private final ListView<SelectItem> conList = new ListView<>();
    private final ListView<SelectItem> schemeList = new ListView<>();

    @Override
    public void init() {
        initData();
        var topBox = new VBox();
        var botBox = new HBox();
        infoPage.setTop(topBox);
        infoPage.setBottom(botBox);
        textArea.setEditable(false);
        infoPage.setCenter(textArea);
        botBox.getChildren().add(bar);
        infoPage.getStyleClass().add("info-page");
        selectorBox.getStyleClass().add("selector-box");
        selectorBox.getChildren().addAll(conList, schemeList);
        bar.prefWidthProperty().bind(infoPage.widthProperty());
        back.setOnAction(event -> {
            textArea.clear();
            bar.setProgress(0d);
            pagination.setCurrentPageIndex(0);
        });
        pagination.setPageFactory(index -> index == 0 ? selectorBox : infoPage);
        pagination.currentPageIndexProperty().addListener((observable, oldValue, newValue) -> {
            var index = newValue.intValue();
            if (index == 1) {
                back.setVisible(true);
                next.setText("开始");
            } else {
                back.setVisible(false);
            }
        });
        toggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) -> {
            if (!(newValue instanceof RadioButton)) {
                return;
            }
            var item = (SelectItem) ((RadioButton) newValue).getParent();
            intent.setTarUUID(item.getUuid());
            intent.setTarScheme(item.getName());
            if (next.isDisable()) {
                next.setDisable(false);
            }
        });
    }

    private void initData() {
        var cons = DbPreference.getParams();
        cons.forEach(client -> {
            var cc = DatabaseFX.DATABASE_SOURCE.getClient(client.getUuid());
            if (cc == null) {
                return;
            }
            var uuid = client.getUuid();
            var name = cc.getConnectionParam().getName();
            var item = new SelectItem(name, uuid, false);
            conList.getItems().add(item);
        });
    }

    private void loadSchemeList(String uuid) {
        var client = DatabaseFX.DATABASE_SOURCE.getClient(uuid);
        var dql = client.getDql();
        var future = dql.showDatabase();
        future.onComplete(ar -> {
            if (ar.failed()) {
                DialogUtils.showErrorDialog(ar.cause(), "加载Scheme列表失败!");
                return;
            }
            var list = ar.result().stream().map(s -> new SelectItem(s, uuid, true))
                    .collect(Collectors.toList());
            Platform.runLater(() -> {
                if (schemeList.getItems().size() > 0) {
                    schemeList.getItems().clear();
                }
                //clear previous toggles
                this.toggleGroup.getToggles().clear();
                for (SelectItem selectItem : list) {
                    schemeList.getItems().add(selectItem);
                    this.toggleGroup.getToggles().add(selectItem.radioButton);
                }
            });
        });
    }

    @FXML
    public void nextOrStart() {
        if (pagination.getCurrentPageIndex() == 0) {
            pagination.setCurrentPageIndex(1);
            return;
        }
        var future = getTableDDL().compose(this::createNewTable);
        future.onComplete(ar -> {
            if (ar.failed()) {
                var throwable = ar.cause();
                this.logger.error("Transfer table failed!", throwable);
                var msg = String.format("Transfer table failed cause:%s.", throwable.getMessage());
                updateInfo(msg, 0d);
            } else {
                Platform.runLater(() -> {
                    var from = intent.getCurTable() + "(" + intent.getCurScheme() + ")";
                    var to = intent.getCurTable() + "(" + intent.getTarScheme() + ")";
                    var msg = String.format("Success transfer table from %s to %s.", from, to);
                    updateInfo(msg, 1);
                });
            }
        });
    }

    private Future<String> getTableDDL() {
        updateInfo("Start query current table ddl statement.", 0.3);
        var promise = Promise.<String>promise();
        var client = DatabaseFX.DATABASE_SOURCE.getClient(intent.getCurUUID());
        client.getDdl()
                .showDDL(intent.getCurScheme(), intent.getCurTable())
                .onComplete(promise);
        return promise.future();
    }

    private Future<Void> createNewTable(String ddl) {
        updateInfo("Start Execute DDL statement to target scheme.", 0.5);
        var promise = Promise.<Void>promise();
        var client = DatabaseFX.DATABASE_SOURCE.getClient(intent.getTarUUID());
        //Update ddl scheme
        try {
            ddl = client.getDdl().updateDDLScheme(ddl, intent.getTarScheme());
        } catch (JSQLParserException e) {
            return Future.failedFuture(e);
        }
        var future = client.execute(ddl);
        future.onComplete(ar -> {
            if (ar.failed()) {
                promise.fail(ar.cause());
            } else {
                promise.complete();
            }
        });
        return promise.future();
    }

    private void updateInfo(String text, double progress) {
        var tt = textArea.getText();
        final String t;
        if (!StringUtils.isEmpty(tt)) {
            t = "\r\n" + text;
        } else {
            t = text;
        }
        Platform.runLater(() -> {
            textArea.appendText(t);
            bar.setProgress(progress);
        });
    }

    public class SelectItem extends HBox {

        private final String name;
        private final String uuid;
        private final RadioButton radioButton = new RadioButton();

        public SelectItem(String name, String uuid, boolean selectModel) {
            this.name = name;
            this.uuid = uuid;
            var label = new Label();
            label.setText(name);
            getStyleClass().add("select-item");
            if (selectModel) {
                getChildren().add(radioButton);
            } else {
                addEventFilter(MouseEvent.MOUSE_CLICKED, event -> loadSchemeList(uuid));
            }
            getChildren().add(label);
        }

        public String getName() {
            return name;
        }

        public String getUuid() {
            return uuid;
        }
    }
}
